import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;

public class assemble extends Assembler{

	Hashtable<String, Integer> labeltabletext = new Hashtable<String, Integer>();
	Hashtable<String, Integer> labeltabledata = new Hashtable<String, Integer>();
    Hashtable<String, String> opcodeMap = new Hashtable<String, String>();
    Hashtable<String, String> registerMap = new Hashtable<String, String>();
    
    public assemble(String [] arg) throws IOException{
    	super(arg);
    }
	
    public static void main(String[] arg) throws IOException
    {
        assemble assembler = new assemble(arg);
        assembler.AssembleCode(arg);
        
        System.out.println(assembler.sourceFile.readLine());
    }

    
	// The student has to implement it for generating the machine codes.
	@Override
	String generateCode(Instruction instruction) {
		String opcode = "";
		
		opcode = opcodeMap.get(instruction.operator);
		
		// J Type
		if (instruction.operator.equals("jmp")) {
			String immediate = Long.toBinaryString(instruction.operands[0].extractImmediate());

			if(immediate.length()>12)
			  immediate = immediate.substring(immediate.length() - 12);

			// prepend leading zeroes to binary immediate
			String leadingZeroes =""; 
			for (int i=0; i < 12-immediate.length(); i++)
				leadingZeroes += "0";
			
			return opcode + leadingZeroes + immediate;
		
		}
		// J-reg Type
		else if(instruction.operator.equals("jmpreg")) {
			String register = registerMap.get(instruction.operands[0].name);
			
			return opcode + register + "000000000";
		}
		// A1 type
		else if (opcode.length() == 6) {
			char b1 = opcode.charAt(4);
			char b2 = opcode.charAt(5);
			
			// 2 register operands
			if (b1 == '0' && b2 == '0') {
				String dest = registerMap.get(instruction.operands[0].name);
				String src = registerMap.get(instruction.operands[1].name);
				String offset = "00000";
				return opcode + dest + src + offset;
			}
			
			// 3 register operands
			else if (b1 == '0' && b2 == '1') {
				String dest = registerMap.get(instruction.operands[0].name);
				String src1 = registerMap.get(instruction.operands[1].name);
				String src2 = registerMap.get(instruction.operands[2].name) + "00";
				return opcode + dest + src1 + src2;
			}
		}
		
		// A2 type or special A1 type
		else if (opcode.length() == 5) {
			char b1 = opcode.charAt(4);
			
			// Special A1 type: ld, st, mov, in, out
			if (b1 == '0') {
				String dest = registerMap.get(instruction.operands[0].name);
				String src = registerMap.get(instruction.operands[1].name);
				String offset = Integer.toBinaryString((instruction.operands[1].offset));
				// prepend leading zeroes to binary immediate
				String leadingZeroes =""; 
					for (int i=0; i < 5-offset.length(); i++)
						leadingZeroes += "0";
				return opcode + "0" + dest + src + leadingZeroes + offset;
			}
			
			// A2 type
			else if (b1 == '1') {
				String dest = registerMap.get(instruction.operands[0].name);
				String immediate = Long.toBinaryString(instruction.operands[1].extractImmediate());
				
				if(immediate.length()>9)
					immediate = immediate.substring(immediate.length() - 9);
				
				// prepend leading zeroes to binary immediate
				String leadingZeroes =""; 
				for (int i=0; i < 9-immediate.length(); i++)
						leadingZeroes += "0";
					
				return opcode + dest + leadingZeroes + immediate;
			}
		}
		
		// B type or halt
		else if (opcode.length() == 4) {
			
			// Special case for halt
			if (instruction.operator.equals("halt"))
				return opcode + "0000000000000";
			
			String src1 = registerMap.get(instruction.operands[0].name);
			String src2 = registerMap.get(instruction.operands[1].name);
			String immediate = Long.toBinaryString(instruction.operands[2].extractImmediate());
			if(immediate.length()>7)
				immediate = immediate.substring(immediate.length() - 7);

			// prepend leading zeroes to binary immediate
			String leadingZeroes =""; 
				for (int i=0; i < 7-immediate.length(); i++)
					leadingZeroes += "0";
			
			return opcode + src1 + src2 + leadingZeroes + immediate;  
		}
		
		else {
			System.err.println("opcode messed up, WTH!");
		}
					
		return null;
	}
	
	// The student has to implement it for initializing some of their own variables and data structures.
	@Override
	void initialization() throws IOException {
		// Map for opcodes
		opcodeMap.put("add2", "0000" + "0" + "0");
		opcodeMap.put("add3", "0000" + "0" + "1");
		opcodeMap.put("addi", "0000" + "1");
		
		opcodeMap.put("sub2", "0001" + "0" + "0");
		opcodeMap.put("sub3", "0001" + "0" + "1");
		opcodeMap.put("subi", "0001" + "1");
		
		opcodeMap.put("nor2", "0010" + "0" + "0");
		opcodeMap.put("nor3", "0010" + "0" + "1");
		opcodeMap.put("nori", "0010" + "1");
		
		opcodeMap.put("sll2", "0011" + "0" + "0");
		opcodeMap.put("sll3", "0011" + "0" + "1");
		opcodeMap.put("slli", "0011" + "1");
		
		opcodeMap.put("slr2", "0100" + "0" + "0");
		opcodeMap.put("slr3", "0100" + "0" + "1");
		opcodeMap.put("slri", "0100" + "1");
		
		opcodeMap.put("in", "0101" + "0");
		opcodeMap.put("ini", "0101" + "1");
		
		opcodeMap.put("out", "0110" + "0");
		opcodeMap.put("outi", "0110" + "1");
		
		opcodeMap.put("ld", "0111" + "0");
		opcodeMap.put("ldl", "0111" + "1");
		opcodeMap.put("st", "1000" + "0");
		opcodeMap.put("stl", "1000" + "1");
		
		opcodeMap.put("halt", "1001");
		
		opcodeMap.put("mov", "1010" + "0");
		opcodeMap.put("movi", "1010" + "1");
		
		opcodeMap.put("ble", "1011");
		opcodeMap.put("jmp", "11000");
		opcodeMap.put("jmpreg", "11001");
		opcodeMap.put("be", "1101");
		
		// Map for registers
		registerMap.put("%sp" , "000");
		registerMap.put("%rv" , "001");
		registerMap.put("%at" , "010");
		
		registerMap.put("%r0" , "100");
		registerMap.put("%r1" , "101");
		registerMap.put("%r2" , "110");
		registerMap.put("%r3" , "111");
	}

	//The student has to implement it for processing the labels.
	@Override
	void processLabel(String sourceCode) {
		if(this.currentCodeSection==1){
			labeltabledata.put(sourceCode, this.dataMemoryAddress);
		}else if(this.currentCodeSection==0){
			labeltabletext.put(sourceCode, this.programCounter);
		}else{
			
		}
		
		
	}
	
    // The student has to implement it for replacing the labels used in instruction
	@Override
	void replaceInstructionLabel(Instruction instruction) {
		if(instruction.operator.equals("jmp")){
			int pc = labeltabletext.get(instruction.operands[0].name);
			instruction.operands[0].name = Integer.toString(pc - this.programCounter,10);
			
		}else if(instruction.operator.equals("ble")||instruction.operator.equals("be")){
			int pc = labeltabletext.get(instruction.operands[2].name);
			instruction.operands[2].name = Integer.toString(pc - this.programCounter,10);

		}else if(instruction.operator.equals("stl")||instruction.operator.equals("ldl")){
			int offset = labeltabledata.get(instruction.operands[1].name);
			instruction.operands[1].name = Integer.toString(offset,10);

		}else{
			return;
		}

		
	}
	
    // The student has to implement it for replacing the labels used in memory
	@Override
	void replaceMemoryLabel() {
		Integer address = null;
		for (int i = 0; i < memory.leng(); i++) {
			if (memory.find(i) == null)
				continue;
			if ((address = labeltabletext.get(memory.find(i))) != null)
				memory.addAtIndex("0x" + Integer.toHexString(address), i);
			else if ((address = labeltabledata.get(memory.find(i))) != null)
				memory.addAtIndex("0x" + Integer.toHexString(address), i);
		}
			
		
	}
	// The student has to implement it for updating the program counter.
	@Override
	void updateProgramCounter(Instruction instruction) {
		this.programCounter++;
		//TODO: check for pseudo instructions; if pseudo instr, increment for the appropriate number of times
		
	}

	
}
